package com.cx.utils.pinghe.util;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.*;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Created by Administrator on 2015/11/6.
 */
public class HttpsUtil {

    private static Logger logger = LoggerFactory.getLogger(HttpsUtil.class);

    private static HttpClient client = null;
    static {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(128);
        cm.setDefaultMaxPerRoute(128);
        client = HttpClients.custom().setConnectionManager(cm).build();


    }

    /**
     * 发送一个 Post 请求, 使用指定的字符集编码.
     *
     * @param url
     * @param body
     *            RequestBody
     * @param mimeType
     *            例如 application/xml
     * @param charset
     *            编码
     * @param connTimeout
     *            建立链接超时时间,毫秒.
     * @param readTimeout
     *            响应超时时间,毫秒.
     * @return ResponseBody, 使用指定的字符集编码.
     *
     * @throws ConnectTimeoutException
     *             建立链接超时异常
     * @throws SocketTimeoutException
     *             响应超时
     * @throws Exception
     */
    public static String post(String url, String body, String mimeType,
                              String charset, Integer connTimeout, Integer readTimeout)
            throws ConnectTimeoutException, SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        String result = "";
        try {
            if (StringUtils.isNotBlank(body)) {
                HttpEntity entity = new StringEntity(body, ContentType.create(
                        mimeType, charset));
                post.setEntity(entity);
            }

            HttpResponse res;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = HttpsUtil.client;
                res = client.execute(post);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }

    /**
     * 以字节数组的形式返回合成文件流
     * @param url
     * @param header
     * @param content
     * @return
     * @throws Exception
     */
    public static byte[] postChunked(String url, Map<String, String> header, String content,
                                     String encoding, Map<String, String> ret)
            throws Exception{
        HttpClient client = new DefaultHttpClient();
        HttpPost post = new HttpPost(url);
        for(String key : header.keySet()){
            post.addHeader(key, header.get(key));
        }
        ByteArrayEntity entity = new ByteArrayEntity(content.getBytes(encoding));
        entity.setChunked(true);
        post.setEntity(entity);

        HttpResponse resp = client.execute(post);
        int httpCode = resp.getStatusLine().getStatusCode();
        if(httpCode == 200) {
            HttpEntity respEntity = resp.getEntity();
            byte[] bytes = respEntity != null ? EntityUtils.toByteArray(respEntity) : null;
            String contentType = resp.getFirstHeader("content-type").getValue();
            if(bytes == null){
                ret.put("err", "不完成的chunked数据");
            }
            ret.put("content-type", contentType);
            logger.info("返回的contentType为：{}", contentType);
            return bytes;
        }else{
            ret.put("err", String.format("http chunked请求返回码：%s 返回描述：%s",
                    resp.getStatusLine().getStatusCode(), resp.getStatusLine().getReasonPhrase()));
            return null;
        }
    }

    /**
     * 提交form表单
     *
     * @param url
     * @param params
     * @param connTimeout
     * @param readTimeout
     * @return
     * @throws ConnectTimeoutException
     * @throws SocketTimeoutException
     * @throws Exception
     */
    public static String postForm(String url, Map<String, String> params,
                                  Map<String, String> headers, Integer connTimeout,
                                  Integer readTimeout) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {

        HttpClient client = null;

        HttpPost post = new HttpPost(url);
        try {
            if (params != null && !params.isEmpty()) {
                List<NameValuePair> formParams = new ArrayList<NameValuePair>();
                Set<Entry<String, String>> entrySet = params.entrySet();
                for (Entry<String, String> entry : entrySet) {
                    formParams.add(new BasicNameValuePair(entry.getKey(), entry
                            .getValue()));
                }
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(
                        formParams, Consts.UTF_8);
                post.setEntity(entity);
            }
            if (headers != null && !headers.isEmpty()) {
                for (Entry<String, String> entry : headers.entrySet()) {
                    post.addHeader(entry.getKey(), entry.getValue());
                }
            }
            HttpResponse res = null;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = HttpsUtil.client;
                res = client.execute(post);
            }
            return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
    }

    /**
     * 发送一个 GET 请求
     *
     * @param url
     * @param charset
     * @return
     * @throws Exception
     */
    public static String get(String url, String charset) throws Exception {
        return get(url, charset,null, null, null);
    }

    public static String get(String url,Map<String,String> headers) throws Exception{
        return get(url,"utf8",headers,null,null);
    }

    /**
     * 发送一个 GET 请求
     *
     * @param url
     * @param charset
     * @param connTimeout
     *            建立链接超时时间,毫秒.
     * @param readTimeout
     *            响应超时时间,毫秒.
     * @return
     * @throws ConnectTimeoutException
     *             建立链接超时
     * @throws SocketTimeoutException
     *             响应超时
     * @throws Exception
     */
    public static String get(String url, String charset,Map<String,String> headers, Integer connTimeout,
                             Integer readTimeout) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {
        HttpClient client = null;

        HttpGet get = new HttpGet(url);
        if (headers != null && !headers.isEmpty()) {
            for (Entry<String, String> entry : headers.entrySet()) {
                get.addHeader(entry.getKey(), entry.getValue());
            }
        }
        String result = "";
        try {

            HttpResponse res = null;

            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(get);
            } else {
                // 执行 Http 请求.
                client = HttpsUtil.client;
                res = client.execute(get);
            }

            result = IOUtils.toString(res.getEntity().getContent(), charset);
        }catch (Exception e){
            e.printStackTrace();
        } finally{
            get.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }

    /**
     * get
     * @param url url
     * @param socketTimeout 超时时间
     * @return
     * @throws IOException
     */
    public static HttpResponse getMethod(String url, int connectTimeout, int socketTimeout) throws IOException {
        HttpClient client = null;
        HttpResponse res = null;
        HttpGet get = new HttpGet(url);
        try {
            RequestConfig.Builder builder = RequestConfig.custom();
            if(connectTimeout > 0){
                builder.setConnectTimeout(connectTimeout * 1000);
            }
            if(socketTimeout > 0){
                builder.setSocketTimeout(socketTimeout * 1000);
            }
            get.setConfig(builder.build());
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(get);
            } else {
                // 执行 Http 请求.
                client = HttpsUtil.client;
                res = client.execute(get);
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally{
            get.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return res;
    }

    /**
     * 从 response 里获取 charset
     *
     * @param ressponse
     * @return
     */
    @SuppressWarnings("unused")
    private static String getCharsetFromResponse(HttpResponse ressponse) {
        // Content-Type:text/html; charset=GBK
        if (ressponse.getEntity() != null
                && ressponse.getEntity().getContentType() != null
                && ressponse.getEntity().getContentType().getValue() != null) {
            String contentType = ressponse.getEntity().getContentType()
                    .getValue();
            if (contentType.contains("charset=")) {
                return contentType
                        .substring(contentType.indexOf("charset=") + 8);
            }
        }
        return null;
    }

    private static CloseableHttpClient createSSLInsecureClient()
            throws GeneralSecurityException {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(
                    null, new TrustStrategy() {
                        public boolean isTrusted(X509Certificate[] chain,
                                                 String authType) throws CertificateException {
                            return true;
                        }
                    }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslContext, new X509HostnameVerifier() {

                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }

                @Override
                public void verify(String host, SSLSocket ssl)
                        throws IOException {
                }

                @Override
                public void verify(String host, X509Certificate cert)
                        throws SSLException {
                }

                @Override
                public void verify(String host, String[] cns,
                                   String[] subjectAlts) throws SSLException {
                }

            });
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (GeneralSecurityException e) {
            throw e;
        }
    }


    public static String postRedPack(String url, String body, String mimeType,
                                     String charset, Integer connTimeout, Integer readTimeout,String keyStorePath,String password)
            throws ConnectTimeoutException, SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        String result = "";
        try {
            if (StringUtils.isNotBlank(body)) {
                HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
                post.setEntity(entity);
            }

            HttpResponse res;
            if (url.startsWith("https")) {

                //String type=KeyStore.getDefaultType();
                KeyStore keyStore  = KeyStore.getInstance("PKCS12");
                FileInputStream instream = new FileInputStream(new File(keyStorePath));
                try {
                    keyStore.load(instream, password.toCharArray());
                } finally {
                    instream.close();
                }

                // Trust own CA and all self-signed certs
                SSLContext sslcontext = SSLContexts.custom()
                        .loadKeyMaterial(keyStore, password.toCharArray())
                        .build();
                // Allow TLSv1 protocol only
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                        sslcontext,
                        new String[] { "TLSv1" },
                        null,
                        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
                client = HttpClients.custom()
                        .setSSLSocketFactory(sslsf)
                        .build();

                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = HttpsUtil.client;
                res = client.execute(post);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }

    public static String postUnifiedorder(String url, String body, String mimeType,
                                     String charset, Integer connTimeout, Integer readTimeout)
            throws Exception {
        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        String result = "";
        try {
            if (StringUtils.isNotBlank(body)) {
                HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
                post.setEntity(entity);
            }
            client = HttpsUtil.client;
            HttpResponse res = client.execute(post);
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            post.releaseConnection();
//            if (url.startsWith("https") && client != null
//                    && client instanceof CloseableHttpClient) {
//                ((CloseableHttpClient) client).close();
//            }
        }
        return result;
    }

    /**
      * TODO 下载文件到本地
      * 
      * @param fileUrl 远程地址
      * @param fileLocal 本地路径
      * @throws Exception
      */
    public static void downloadFile(String fileUrl, String fileLocal) throws Exception {
    SSLContext sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
    sslcontext.init(null, new TrustManager[] { new X509TrustUtiil() }, new java.security.SecureRandom());
    URL url = new URL(fileUrl);
    HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() {
    public boolean verify(String s, SSLSession sslsession) {
    System.out.println("WARNING: Hostname is not matched for cert.");
    return true;
    }
    };
    HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier);
    HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
    HttpsURLConnection urlCon = (HttpsURLConnection) url.openConnection();
    urlCon.setConnectTimeout(60000);
    urlCon.setReadTimeout(60000);
    int code = urlCon.getResponseCode();
    if (code != HttpURLConnection.HTTP_OK) {
    throw new Exception("文件读取失败");
    }
    // 读文件流
    DataInputStream in = new DataInputStream(urlCon.getInputStream());
    DataOutputStream out = new DataOutputStream(new FileOutputStream(fileLocal));
    byte[] buffer = new byte[2048];
    int count = 0;
    while ((count = in.read(buffer)) > 0) {
    out.write(buffer, 0, count);
    }
    out.close();
    in.close();
    }


    //接口返回图片接口
    //将接口返回图片进行保存
    public static void downloadFile(String fileUrl, String fileLocal,String jwt) throws Exception {
        SSLContext sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
        sslcontext.init(null, new TrustManager[] { new X509TrustUtiil() }, new java.security.SecureRandom());
        URL url = new URL(fileUrl);
        HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() {
            public boolean verify(String s, SSLSession sslsession) {
                System.out.println("WARNING: Hostname is not matched for cert.");
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier);
        HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
        HttpsURLConnection urlCon = (HttpsURLConnection) url.openConnection();
        urlCon.setRequestProperty("Cookie",jwt);
        urlCon.setConnectTimeout(60000);
        urlCon.setReadTimeout(60000);
        int code = urlCon.getResponseCode();
        if (code != HttpURLConnection.HTTP_OK) {
            throw new Exception("文件读取失败");
        }
        // 读文件流
        DataInputStream in = new DataInputStream(urlCon.getInputStream());
        DataOutputStream out = new DataOutputStream(new FileOutputStream(fileLocal));
        byte[] buffer = new byte[2048];
        int count = 0;
        while ((count = in.read(buffer)) > 0) {
            out.write(buffer, 0, count);
        }
        out.close();
        in.close();
    }



    public static String post(String url,String body,Map<String,String> headers) throws Exception{
        return post(url,body,headers,"application/json","utf8",10000,10000);
    }

    public static String post(String url, String body,Map<String,String> headers, String mimeType,
                              String charset, Integer connTimeout, Integer readTimeout)
            throws ConnectTimeoutException, SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        if (headers != null && !headers.isEmpty()) {
            for (Entry<String, String> entry : headers.entrySet()) {
                post.addHeader(entry.getKey(), entry.getValue());
            }
        }
        post.setHeader("Connection", "close");
        String result = "";
        try {
            if (StringUtils.isNotBlank(body)) {
                HttpEntity entity = new StringEntity(body, ContentType.create(
                        mimeType, charset));
                post.setEntity(entity);
            }

            HttpResponse res;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = HttpsUtil.client;
                res = client.execute(post);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }

    public static String put(String url,String body,Map<String,String> headers) throws Exception{
        return put(url,body,headers,"application/json","utf8",10000,10000);
    }
    public static String put(String url, String body,Map<String,String> headers, String mimeType,
                              String charset, Integer connTimeout, Integer readTimeout)
            throws ConnectTimeoutException, SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpPut put = new HttpPut(url);
        if (headers != null && !headers.isEmpty()) {
            for (Entry<String, String> entry : headers.entrySet()) {
                put.addHeader(entry.getKey(), entry.getValue());
            }
        }
        put.setHeader("Connection", "close");
        String result = "";
        try {
            if (StringUtils.isNotBlank(body)) {
                HttpEntity entity = new StringEntity(body, ContentType.create(
                        mimeType, charset));
                put.setEntity(entity);
            }

            HttpResponse res;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(put);
            } else {
                // 执行 Http 请求.
                client = HttpsUtil.client;
                res = client.execute(put);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            put.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }

    public static String delete(String url,Map<String,String> headers) throws Exception{
        return delete(url,"utf8",headers,null,null);
    }

    /**
     * 发送一个 GET 请求
     *
     * @param url
     * @param charset
     * @param connTimeout
     *            建立链接超时时间,毫秒.
     * @param readTimeout
     *            响应超时时间,毫秒.
     * @return
     * @throws ConnectTimeoutException
     *             建立链接超时
     * @throws SocketTimeoutException
     *             响应超时
     * @throws Exception
     */
    public static String delete(String url, String charset,Map<String,String> headers, Integer connTimeout,
                             Integer readTimeout) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {
        HttpClient client = null;

        HttpDelete delete = new HttpDelete(url);
        if (headers != null && !headers.isEmpty()) {
            for (Entry<String, String> entry : headers.entrySet()) {
                delete.addHeader(entry.getKey(), entry.getValue());
            }
        }
        String result = "";
        try {

            HttpResponse res = null;

            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(delete);
            } else {
                // 执行 Http 请求.
                client = HttpsUtil.client;
                res = client.execute(delete);
            }

            result = IOUtils.toString(res.getEntity().getContent(), charset);
        }catch (Exception e){
            e.printStackTrace();
        } finally{
            delete.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }
}
